package com.xbongbong.pro.message.listener;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.NamedThreadFactory;
import com.xbongbong.paas.toolbox.util.OkHttpUtil;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.sms.model.SmsConfigModel;
import com.xbongbong.pro.sms.model.SmsStatusModel;
import com.xbongbong.saas.domain.entity.SmsStatusEntity;
import com.xbongbong.saas.enums.SmsStatusEnum;
import com.xbongbong.saas.model.DynamicModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.annotation.Resource;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author youli.chen
 * @version v1.0
 * @description: 短信定时发送处理任务
 * @date 2019/3/26 10:45
 * @since v1.0
 */
@WebListener
public class SmsDelayedSendListener implements ServletContextListener {
    private static final Logger LOG = LoggerFactory.getLogger(SmsDelayedSendListener.class);

    private SmsStatusModel smsStatusModel;

    private SmsConfigModel smsConfigModel;
    @Resource
    private DynamicModel dynamicModel;

    /**
     * 应用地址
     */
    private static final String URI = "http://smssh1.253.com/msg/send/json";

    /**
     * 限制最大线程数10
     */
    private ExecutorService executor = new ThreadPoolExecutor(0, 10,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1024),
            new NamedThreadFactory("kafka-web-sms"),
            new ThreadPoolExecutor.CallerRunsPolicy());

    private ExecutorService executorDynamic = new ThreadPoolExecutor(0, 10,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1024),
            new NamedThreadFactory("listener-dynamic-sms"),
            new ThreadPoolExecutor.CallerRunsPolicy());

    private ScheduledExecutorService executorLoop = null;


    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
        smsStatusModel = (SmsStatusModel) ctx.getBean("smsStatusModel");
        smsConfigModel = (SmsConfigModel) ctx.getBean("smsConfigModel");
        dynamicModel = ctx.getBean("dynamicModel", DynamicModel.class);
        try {
            LOG.info("--------开始短信定时发送处理任务---------");
            taskInsertQueue();
            LOG.info("--------结束短信定时发送处理任务---------");
        } catch (Exception e) {
            LOG.error("taskInsertQueue 启动失败", e);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        try {
            loopClose();
        } catch (Exception e) {
            LOG.error("taskInsertQueue 停止失败", e);
        }
    }

    private void taskInsertQueue() throws Exception {
        if (executorLoop == null) {
            executorLoop = Executors.newScheduledThreadPool(1);
        }
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Map<String, Object> param = new HashMap<>(16);
                param.put("del", DelEnum.NORMAL.getDel());
                //未发送
                param.put("status", SmsStatusEnum.NO_SEND.getCode());
                //定时发送
                param.put("type", 1);
                //现在到未来1分钟的定时短信
                Integer now = DateUtil.getInt();
                //时间
                Integer startTime = now;
                //1分钟
                Integer endTime = startTime + 60;
                param.put("startTime", startTime);
                param.put("endTime", endTime);
                List<SmsStatusEntity> lists = smsStatusModel.findEntitys(param);
                //入队列
                for (SmsStatusEntity entity : lists) {
                    LOG.info("--------开始写库更新短信定时发送处理任务---------" + entity);
                    insertQueue(entity);
                    LOG.info("--------结束写库更新短信定时发送处理任务---------" + entity);
                }
            }
        };
        LOG.info("loop start");
        executorLoop.scheduleWithFixedDelay(runnable, 0, 1, TimeUnit.MINUTES);
    }

    /**
     * @throws Exception
     * @author tzl
     * @date 2016年12月6日 上午10:32:49
     */
    private void loopClose() throws Exception {
        LOG.info("loop close");
        executorLoop.shutdown();
        executorLoop = null;
    }

    /**
     * 立即发送的短信进入消息队列
     *
     * @param entity
     */
    private void insertQueue(final SmsStatusEntity entity) {
        executor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    String mobile = entity.getMobile();
                    String content = entity.getContent();
                    Integer sendApi = entity.getSendApi();
                    if (sendApi == null) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100019, "创蓝营销帐号不能为空！");
                    }
                    if (Objects.equals(entity.getStatus(), SmsStatusEnum.SEND_ING.getCode())) {
                        //短信为已发送状态，不再发送短信
                        LOG.error("短信重复发送，被我阻止！记录实体id : ", entity.getId());
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, "短信重复发送，被我阻止！");
                    }
                    switch (sendApi) {
                        case 2:
                            JSONObject marketRetJson = sendSMS(mobile, content);
                            // 处理短信返回状态入库
                            dealJsonRet(marketRetJson, entity);
                            break;
                        case 1:
                            // 发短信
                            JSONObject noticeRetJson = sendSmsMsg(mobile, content);
                            // 处理短信返回状态入库
                            dealJsonRet(noticeRetJson, entity);
                            break;
                        case 0:
                        default:
                            LOG.error("短信发送sendApi:case 0已经终止服务 : ", sendApi);
                    }
                    executorDynamic.execute(() -> dynamicModel.insertSmsSendDynamic(entity));
                } catch (Exception e) {
                    LOG.error("发送信息到kafka时出错", e);
                }
            }
        });
    }

    /**
     * 2：营销号
     *
     * @param mobiles 手机号
     * @param content 短信内容
     * @return
     */
    public JSONObject sendSMS(String mobiles, String content) {
        // encodeURIComponent url编码替换+
        content = content.replaceAll("\\+", "%2B");
        String account = "M1252607";
        String password = "2lywWNBgCZfb66";
        SmsSendRequest smsSingleRequest = new SmsSendRequest(account, password, content, mobiles);
        String requestJson = JSON.toJSONString(smsSingleRequest);
        String ret = OkHttpUtil.post(URI, requestJson);
        return JSONObject.parseObject(ret);
    }

    /**
     * 1：调试号--正在用
     *
     * @param mobiles 手机号
     * @param content 短信内容
     * @return
     */
    public JSONObject sendSmsMsg(String mobiles, String content) {
        // encodeURIComponent url编码替换+
        content = content.replaceAll("\\+", "%2B");
        String account = "N8010355";
        String password = "p84jDXw1A";
        SmsSendRequest smsSingleRequest = new SmsSendRequest(account, password, content, mobiles);
        String requestJson = JSON.toJSONString(smsSingleRequest);
        String ret = OkHttpUtil.post(URI, requestJson);
        return JSONObject.parseObject(ret);
    }

    /**
     * @param ret    回调返回数据
     * @param entity 回调的短信实体
     *               创建时间： 很早以前
     *               修改时间： 2018-1-2 下午4:19:18 chy
     * @Description: 短信发送回调处理
     * @author
     * @version v3.15.1
     * @since 很早以前
     */
    private void dealJsonRet(JSONObject ret, SmsStatusEntity entity) throws XbbException {
        if (ret == null || entity == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100019, "短信回调数据不能为空！");
        }
        Integer code = ret.getInteger("code");
        if (code == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100019, "短信回调返回数据code值不能为空！");
        }
        String msgId = ret.getString("msgId");
        try {
            if (code == 0) {
                // 创蓝成功返回
                entity.setSendTime((long) DateUtil.getInt());
                entity.setStatus(SmsStatusEnum.SEND_ING.getCode());
                smsConfigModel.deductFee(entity.getCorpid(), entity.getContent());
            } else {
                entity.setSendTime((long) DateUtil.getInt());
                msgId = "err--" + code + "--" + DateUtil.getInt();
                entity.setStatus(SmsStatusEnum.FAILURE_SEND.getCode());
                entity.setStatusMemo(ret.getString("errorMsg"));
            }
            entity.setMsgId(msgId);
            entity.setUpdateTime((long) DateUtil.getInt());
            smsStatusModel.update(entity);
        } catch (Exception e) {
            LOG.error("dealJsonRet短信回调异常", e);
        }
    }

    /**
     * @author tianyh
     * @date 2017年4月15日 下午3:41:44
     * @Title: SmsSingleRequest
     * @ClassName: SmsSingleRequest
     * @Description:普通短信发送
     */
    class SmsSendRequest {

        /**
         * 用户账号，必填
         */
        private String account;

        /**
         * 用户密码，必填
         */
        private String password;

        /**
         * 短信内容。长度不能超过536个字符，必填
         */
        private String msg;

        /**
         * 机号码。多个手机号码使用英文逗号分隔，必填
         */
        private String phone;

        /**
         * 定时发送短信时间。格式为yyyyMMddHHmm，值小于或等于当前时间则立即发送，默认立即发送，选填
         */
        private String sendtime;

        /**
         * 是否需要状态报告（默认false），选填
         */
        private Boolean report;

        /**
         * 下发短信号码扩展码，纯数字，建议1-3位，选填
         */
        private String extend;

        /**
         * 该条短信在您业务系统内的ID，如订单号或者短信发送记录流水号，选填
         */
        private String uid;

        public SmsSendRequest() {
        }

        public SmsSendRequest(String account, String password, String msg, String phone) {
            super();
            this.account = account;
            this.password = password;
            this.msg = msg;
            this.phone = phone;
            this.report = true;
        }

        public String getAccount() {
            return account;
        }

        public void setAccount(String account) {
            this.account = account;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }

        public String getSendtime() {
            return sendtime;
        }

        public void setSendtime(String sendtime) {
            this.sendtime = sendtime;
        }

        public Boolean getReport() {
            return report;
        }

        public void setReport(Boolean report) {
            this.report = report;
        }

        public String getExtend() {
            return extend;
        }

        public void setExtend(String extend) {
            this.extend = extend;
        }

        public String getUid() {
            return uid;
        }

        public void setUid(String uid) {
            this.uid = uid;
        }
    }
}
